Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.

...powered by www.netzwerkartist.de...

 <<   zurück
Visual Basic 2005 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual Basic 2005

Visual Basic 2005
1.233 S., mit 2 CDs, 59,90 Euro
Galileo Computing
ISBN 3-89842-585-1
gp Kapitel 9 Fehlerbehandlung und Debugging
  gp 9.1 Die Behandlung von Laufzeitfehlern
    gp 9.1.1 Laufzeitfehler erkennen
    gp 9.1.2 Die Behandlung von Exceptions
    gp 9.1.3 Die »Try ... Catch«-Anweisung
    gp 9.1.4 Behandlung mehrerer Exceptions
    gp 9.1.5 Die »Finally«-Anweisung
    gp 9.1.6 Das Weiterleiten von Ausnahmen
    gp 9.1.7 Die Hierarchie der Exceptions
    gp 9.1.8 Die Reihenfolge der »Catch«-Klauseln
    gp 9.1.9 Die Basisklasse »Exception«
    gp 9.1.10 Benutzerdefinierte Exceptions
  gp 9.2 Debuggen mit Programmcode
    gp 9.2.1 Einführung
    gp 9.2.2 Die Klasse »Debug«
    gp 9.2.3 Die Klasse »Trace«
    gp 9.2.4 Ablaufverfolgung mit »TraceListener«-Objekten
    gp 9.2.5 Steuerung der Protokollierung mit Schaltern
    gp 9.2.6 Bedingte Kompilierung
  gp 9.3 Debuggen mit dem Visual Studio 2005
    gp 9.3.1 Debuggen im Haltemodus
    gp 9.3.2 Das »Befehlsfenster«
    gp 9.3.3 Weitere Alternativen, um Variableninhalte zu prüfen
  gp 9.4 Das Objekttestcenter (Object Test Bench – OTB)


Galileo Computing

9.2 Debuggen mit Programmcode  downtop


Galileo Computing

9.2.1 Einführung  downtop

In Abschnitt 9.1 haben wir uns mit Fehlern beschäftigt, die nach der erfolgreichen Kompilierung zur Laufzeit auftreten können und, falls sie nicht behandelt werden, unweigerlich zum Absturz des Programms führen. Vielleicht noch schlimmer sind Fehler, die weder vom Compiler erkannt werden, noch einen Laufzeitfehler verursachen. Es sind die logischen Fehler, die ein falsches oder zumindest unerwartetes Ergebnis zur Folge haben. Um logische Fehler aufzuspüren, muss die Anwendung unter Zuhilfenahme des integrierten Debuggers untersucht werden.

Das .NET Framework stellt Ihnen eine Reihe von Hilfsmitteln zur Verfügung, um den Programmcode zu debuggen. Die Spanne reicht von der einfachen Ausgabe von Meldungen im Ausgabe-Fenster bis zur Umleitung der Meldungen in eine Datei oder das Windows-Ereignisprotokoll. Dabei können Sie das Laufzeitverhalten einer Anwendung sowohl mit Programmcode als auch mit der Unterstützung von Visual Studio 2005 überprüfen. Wir werden in den nächsten Abschnitten auf alle Debugging-Techniken eingehen.


Galileo Computing

9.2.2 Die Klasse »Debug«  downtop

In den vorangegangenen Beispielen haben wir uns sehr häufig eines Kommandos bedient, um beispielsweise den Inhalt von Variablen zu überprüfen. Es war die Methode WriteLine der Klasse Console:


Dim intVar As Integer = 4711
Console.WriteLine(intVar)

Diese Technik hat zur Folge, dass die Ausgabe an der Konsole unübersichtlich wird und zwischen den erforderlichen Programminformationen immer wieder Informationen zu finden sind, die nur der Entwicklungsunterstützung dienen. Bevor ein solches Programm an den Kunden ausgeliefert wird, müssen die Testausgaben aus dem Programmcode gelöscht werden.

Die Entwicklungsumgebung bietet uns eine bessere Alternative an. Dazu wird die Ausgabe nicht in das Konsolenfenster geschrieben, sondern in das Ausgabe-Fenster von Visual Studio 2005. Standardmäßig wird dieses Fenster am unteren Rand der Entwicklungsumgebung angezeigt. Sie können es sich anzeigen lassen, indem Sie im Menü Ansicht den Menüpunkt Ausgabe wählen.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 9.3     Das Fenster »Ausgabe«

Sie haben dieses Fenster möglicherweise schon häufig gesehen und aufmerksam seinen Inhalt gelesen, denn bei jeder Kompilierung werden hier Informationen ausgegeben, beispielsweise ob die Kompilierung fehlerfrei war. Das Ausgabe-Fenster zeigt uns aber nicht nur Informationen an, die der Compiler hineinschreibt, wir können auch eigene Meldungen in dieses Fenster umleiten.

Eine Debug-Information in das Ausgabe-Fenster zu schreiben, ist genauso einfach wie die Ausgabe an der Konsole. Wir müssen nur die Anweisung


Console.WriteLine("...")

durch


Debug.WriteLine("...")

ersetzen. Debug ist eine nicht ableitbare Klasse des Namespace System.Diagnostics, die ausschließlich statische Member bereitstellt.

Die Methode Debug.WriteLine unterscheidet sich von der Methode Console.WriteLine dahingehend, dass sie keine Formatierungsmöglichkeiten erlaubt. Um mehrere Informationen in einer gemeinsamen Zeichenfolge unterzubringen, müssen Sie daher den »+«-Verknüpfungsoperator benutzen:


Debug.WriteLine("Inhalt von iVar = " + obj.iVar)

Programmablaufinformationen anzeigen

Debug.WriteLine ist mehrfach überladen und kann ein Argument vom Typ String oder Object entgegennehmen. Eine parameterlose Überladung gibt es nicht.


Public Shared Sub WriteLine(value As Object)
Public Shared Sub WriteLine(message As String)

Optional können wir auch ein zweites String-Argument übergeben, das eine detaillierte Beschreibung bereitstellt, die vor der eigentlichen Debug-Information ausgegeben wird.


Public Shared Sub WriteLine(value As Object, category As String)
Public Shared Sub WriteLine(message As String, category As String)

Sehen wir uns das an einem Beispiel an. Die Anweisung


Debug.WriteLine("Inhalt von value = " + value, "Variable value")

wird in das Ausgabe-Fenster


Variable value: Inhalt von value = 34

schreiben – vorausgesetzt, der Inhalt von value ist 34.

Neben WriteLine sind in der Klasse Debug noch weitere Methoden zur Ausgabe von Informationen definiert. Die Tabelle 9.2 gibt darüber Auskunft.


Tabelle 9.2     Ausgabemethoden der Klasse »Debug«

Methode Beschreibung
Write Schreibt Debug-Informationen ohne Zeilenumbruch.
WriteLine Schreibt Debug-Informationen mit Zeilenumbruch.
WriteIf Schreibt Debug-Informationen ohne Zeilenumbruch, wenn eine bestimmte Bedingung erfüllt ist.
WriteLineIf Schreibt Debug-Informationen mit Zeilenumbruch, wenn eine bestimmte Bedingung erfüllt ist.

Die beiden zuletzt aufgeführten Methoden WriteIf und WriteLineIf schreiben nur dann Debug-Informationen, wenn eine bestimmte Randbedingung erfüllt ist. Damit lässt sich der Programmcode übersichtlicher gestalten. Beide Methoden sind genauso überladen wie Write bzw. WriteLine, erwarten jedoch im ersten Parameter zusätzlich einen booleschen Wert, z.  B.:


Public Shared Sub WriteIf(condition As Boolean, message As String)

Verdeutlichen wir uns den Einsatz an einem Beispiel. Um den Inhalt des Feldes value zu testen, könnten wir in herkömmlicher Weise codieren:


If (value = 77) Then
Debug.WriteLine("Inhalt von value ist 77")
End If

Mit WriteLineIf wird daraus eine Codezeile:


Debug.WriteLineIf(value = 77, "Inhalt von value ist 77")

Wie Sie später noch sehen, bieten uns die Methoden WriteLineIf und WriteIf sehr bequem einzusetzende Möglichkeiten, eine Ablaufprotokollierung zu steuern.

Einrücken der Ausgabeinformation

Die Klasse Debug stellt uns Eigenschaften und Methoden zur Verfügung, um die Debug-Ausgaben einzurücken. Mit der Methode Indent wird die Einzugsebene um eins erhöht, mit Unindent um eins verringert. Standardmäßig beschreibt eine Einzugsebene vier Leerzeichen. Mit der Eigenschaft IndentSize kann ein anderer Wert bestimmt werden. IndentLevel erlaubt, eine bestimmte Einzugsebene festlegen, ohne Indent mehrfach aufrufen zu müssen.

An einem Beispiel wollen wir uns noch die Auswirkungen ansehen.


Debug.WriteLine("Ausgabe 1")
Debug.Indent()
Debug.WriteLine("Ausgabe 2")
Debug.IndentLevel = 3
Debug.WriteLine("Ausgabe 3")
Debug.Unindent()
Debug.WriteLine("Ausgabe 4")
Debug.IndentSize = 2
Debug.IndentLevel = 1
Debug.WriteLine("Ausgabe 5")

Der Code führt zu folgender Ausgabe:


Ausgabe 1
Ausgabe 2
Ausgabe 3
Ausgabe 4
Ausgabe 5

Die Methode »Assert«

Mit der Methode Assert können Sie eine Annahme prüfen, um beispielsweise unzulässige Zustände festzustellen. Die Methode zeigt eine Fehlermeldung an, wenn ein Ausdruck mit False ausgewertet wird.


Debug.Assert(value >= 0, "value ist negativ")

Hat die Eigenschaft value einen Wert, der kleiner 0 ist, erscheint auf dem Bildschirm die in der folgenden Abbildung gezeigte Nachricht.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 9.4     Die Meldung der Methode »Debug.Assert«

Das Dialogfenster enthält neben der dem zweiten Parameter übergebenen Zeichenfolge auch Informationen darüber, in welcher Klasse und welcher Methode der Assertionsfehler aufgetreten ist.


Galileo Computing

9.2.3 Die Klasse »Trace«  downtop

Die Klasse Trace unterscheidet sich in der Liste ihrer Eigenschaften und Methoden nicht von Debug. Dennoch gibt es einen Unterschied, der sich nur bei einem Wechsel der Build-Konfiguration zwischen Release und Debug bemerkbar macht (siehe Abbildung 9.5).

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 9.5     Die Einstellung der Debug-/Release-Build-Konfiguration

Die Debug-/Release-Konfiguration

Standardmäßig ist bei jedem Projekt die Konfiguration Debug ausgewählt. Anweisungen, die auf den Klassen Debug oder Trace basieren, werden dann grundsätzlich immer bearbeitet. Wird jedoch die Konfiguration Release gewählt, ignoriert der VB-Compiler Aufrufe auf die Klasse Debug, während Aufrufe auf Trace weiterhin bearbeitet werden.

Das ist aber noch nicht das Wesentlichste. Viel wichtiger ist die Tatsache, dass Aufrufe auf Trace kompiliert werden – unabhängig davon, ob Sie die Konfiguration Debug oder Release eingestellt haben. Viele Trace-Anweisungen vergrößern deshalb auch das DLL- bzw. EXE-Kompilat. Andererseits hat der Entwickler hier auch eine einfache Möglichkeit, bestimmte Zustände, die sich zur Laufzeit einstellen und geprüft werden müssen, zu protokollieren.

Unterhalb des Verzeichnisses, in dem sich die Quellcodedateien befinden, legt die Entwicklungsumgebung das Verzeichnis \bin an, dem selbst je nach eingestellter Build-Konfiguration die beiden Verzeichnisse \Debug und \Release untergeordnet sind. Abhängig von der Konfigurationseinstellung wird das Kompilat der ausführbaren Datei in eines dieser beiden Unterverzeichnisse gespeichert.

Debug-Informationen, die beim Kompilieren generiert werden, sind in einer Datei mit der Dateierweiterung .PDB im Verzeichnis gespeichert. Der Debugger nutzt die darin enthaltenen Informationen, um Variablennamen und andere Informationen während des Debuggens in einem sinnvollen Format anzuzeigen.


Galileo Computing

9.2.4 Ablaufverfolgung mit »TraceListener«-Objekten  downtop

Standardmäßig erfolgt die Ausgabe der Ablaufverfolgungsmeldungen im Fenster Ausgabe. Mit dem Schließen des Ausgabe-Fensters gehen auch die Meldungen verloren. Das .NET Framework stellt Ihnen aber auch Konzepte zur Verfügung, um die Meldungen beispielsweise in eine Datei oder das Ereignisprotokoll von Windows umzuleiten. Verantwortlich dafür sind Listener-Objekte.

Im Namespace System.Diagnostics sind fünf Listener vordefiniert, die alle aus der abstrakten Klasse TraceListener abgeleitet sind:

gp  ConsoleTraceListener
gp  DefaultTraceListener
gp  DelimitedListTraceList
gp  TextWriterTraceListener
gp  EventLogTraceListener

Eine Beschreibung der genannten Klassen können Sie der Tabelle 9.3 entnehmen.


Tabelle 9.3     Beschreibung der Listener-Klassen des Namespace System.Diagnostics

Listener-Klasse Beschreibung
ConsoleTraceListener Die ConsoleTraceListener-Klasse schreibt Ablaufverfolgungs- und Debug-Meldungen in die Konsole.
DefaultTraceListener Die Klasse DefaultTraceListener sendet die Ausgabe an die Entwicklungsumgebung. Das ist auch gleichzeitig die Standardeinstellung.
DelimitedListTraceListener Leitet die Ablaufverfolgungs- oder Debug-Ausgabe an einen Textwriter, z.  B. einen Streamwriter, oder in einen Stream.
EventLogTraceListener Die Klasse EventLogTraceListener leitet die Ausgabe an das Windows-Ereignisprotokoll weiter.
TextWriterTraceListener Der Empfänger von TextWriterTraceListener ist ein Stream, beispielsweise um die Informationen in eine Datei zu schreiben.

Das sind noch nicht alle vordefinierten Listener, die das .NET Framework anbietet. Die anderen lassen sich aber schwerlich in einem .NET-Grundlagenteil einbauen, deshalb soll es auch bei der kurzen Erwähnung bleiben. Wenn Ihnen danach ist, können Sie auch eigene Listener definieren. Sie brauchen dazu nur die Klasse TraceListener abzuleiten.

Die Listener-Objekte sind an die Klassen Debug und Trace gebunden. Beide Klassen stellen mit Listeners eine Eigenschaft vom Typ TraceListenerCollection bereit. Diese Auflistung weist die von einer ArrayList bekannten Eigenschaften und Methoden auf, also z.  B. Add, Remove, Clear. Beachten Sie, dass die Listener-Objekte sowohl von Trace als auch von Debug gemeinsam benutzt werden. Beide Klassen bedienen sich also derselben Auflistung.

Sehen wir uns nun an einem Beispiel an, wie die Listener eingesetzt werden.


Debug.Listeners.Clear()
Dim con As TextWriterTraceListener = _
New TextWriterTraceListener(Console.Out)
Debug.Listeners.Add(con)
Debug.WriteLine("Debug")
Trace.WriteLine("Trace")
Console.ReadLine()

In der ersten Anweisung wird die Listeners-Auflistung mit Clear geleert. Weil damit der standardmäßige Eintrag des DefaultTraceListeners-Objekts gelöscht wird, würde ein Debug.Write- oder Trace.Write-Methodenaufruf keinen Abnehmer mehr finden und im Nirwana verpuffen. In der folgenden Codezeile wird daher die Klasse ConsoleTraceListener instanziiert. Dieses Objekt leitet die Ausgabemeldungen an die Konsole um und wird mit der darauf folgenden Anweisung der TraceListenerCollection übergeben.

Da sich die Klassen Trace und Debug die installierten Listener gleichberechtigt teilen, werden in der Build-Konfiguration Debug beide Meldungen an der Konsole angezeigt.

Die Klasse »TextWriterTraceListener«

Eine Liste von insgesamt sieben Konstruktoren zeugt davon, dass die Klasse TextWriterTraceListener die flexibelste der Listener ist. Neben dem parameterlosen Konstruktor kann dieser Listener auch mit einem TextWriter- oder einem Stream-Objekt verbunden werden.


Hinweis

Weitergehende Informationen zu den Writer- und Stream-Klassen erhalten Sie in Kapitel 12.


Nehmen wir an, dass Sie Ihre Debug-Informationen in einer Datei speichern möchten. Im einfachsten Fall brauchen Sie dem Konstruktor nur eine Zeichenfolge zu übergeben, die den Pfad zu der Datei beschreibt. Anschließend muss der neue Listener registriert werden.


Dim listener As TextWriterTraceListener = _
New TextWriterTraceListener("C:\DebugProtocol.txt")
Trace.Listeners.Add(listener)
Debug.WriteLine("Debug.WriteLine-Anweisung")
Trace.WriteLine("Trace.WriteLine-Anweisung")
listener.Flush()
listener.Close()

TextWriterTraceListener schreibt alle Ausgabeinformationen zunächst in einen Puffer. Mit dem Aufruf der Methode Flush wird der Puffer geleert und der Inhalt in die Datei geschrieben. Alternativ dazu können Sie auch die Eigenschaft AutoFlush=True setzen. Mit Close wird das TextWriterTraceListener-Objekt geschlossen. Soll danach die Ablaufverfolgung wieder aufgenommen werden, muss der Listener neu initialisiert und registriert werden.

Die Klassen Debug und Trace bedienen sich derselben Listener. Daher werden beide Write-Anweisungen in der Datei einen Abnehmer finden – vorausgesetzt natürlich, dass die Anwendung im Debug- und nicht im Release-Modus ausgeführt wird.

Ist die Datei bereits vorhanden, werden die Debug-Informationen an den alten Dateiinhalt angehängt. Vielleicht wünschen Sie aber, dass die alten Einträge überschrieben werden. Hier hilft die Überladung des TextWriterTraceListener-Konstruktors weiter, welche die Referenz auf ein Stream-Objekt erwartet.

Im folgenden Beispiel wird dem Konstruktor eine Referenz vom Typ FileStream übergeben. Das FileStream-Objekt leitet die Ausgabe in eine Datei um. Ist die Datei bereits vorhanden, wird die alte überschrieben, ansonsten wird eine neue erzeugt. Dafür sorgt die Angabe FileMode.Create.


' -----------------------------------------------------
' Beispiel: ...\Kapitel 9\TextWriterTraceListenerDemo
' -----------------------------------------------------
Imports System.IO
Module Module1
Sub Main()
Debug.Listeners.Clear()
Dim fs As FileStream = _
New FileStream("C:\ErrorProtocol.txt", FileMode.Create)
Dim listener As TextWriterTraceListener = _
New TextWriterTraceListener(fs)
Debug.Listeners.Add(listener)
Try
Operation()
Catch e As Exception
Dim strError As String = DateTime.Now.ToString()
Trace.WriteLine("Fehler: " + strError + " – " + e.Message)
Finally
listener.Flush()
listener.Close()
End Try
End Sub
Sub Operation()
Throw New Exception("Unbekannter Fehler")
End Sub
End Module

Sehr häufig werden Listener dazu benutzt, aufgetretene Laufzeitfehler im Catch-Zweig der Fehlerbehandlung zu protokollieren. In diesem Beispiel wird in der Methode Operation eine Exception mit einer spezifischen Fehlermeldung erzeugt. Neben der Fehlermeldung werden in der Protokolldatei auch noch Datum und Uhrzeit des Auftretens der Ausnahme festgehalten. Die entsprechende Information liefert uns die statische Methode Now der Klasse DateTime.

Mehrere Listener verwalten

Ein TextWriterTraceListener beschreibt immer genau einen Informationsabnehmer und eignet sich nicht nur dazu, Fehler zu protokollieren. Sie können einen Listener auch genauso gut dazu benutzen, allgemeine Informationen in einen Stream zu schreiben.

Es sind deshalb auch Szenarien vorstellbar, in denen mehrere Listener mit unterschiedlichen Aufgaben benötigt werden. Grundsätzlich werden Meldungen von jedem registrierten Listener gepuffert. Soll eine Meldung nur von einem bestimmter Listener verarbeitet werden, muss die Registrierung der anderen Listener zumindest zeitweise aufgehoben werden. Mit den Methoden Add, Remove sowie dem Indexer ist das grundsätzlich möglich. Allerdings ist es einfacher, ein Listenelement über einen Namen als über den Index in der Auflistung anzusprechen. Daher stellt die Klasse TextWriterTraceListener Konstruktoren mit einem zweiten Parameter bereit, um den Listener zu benennen. Der Name des Listeners wird in der Eigenschaft Name eingetragen.

Im folgenden Beispiel werden zwei Listener registriert und beiden eine Zeichenfolge übergeben. Anschließend wird ein Listener aus der TraceListenerCollection gelöscht. Die anschließende Meldung wird nur vom verbleibenden Listener weitergeleitet.


' den ersten Listener registrieren
Dim Alistener As TextWriterTraceListener = _
New TextWriterTraceListener("C:\A.txt", "AListener")
Trace.Listeners.Add(Alistener)
' den zweiten Listener registrieren
Dim Blistener As TextWriterTraceListener = _
New TextWriterTraceListener("C:\B.txt", "BListener")
Trace.Listeners.Add(Blistener)
Trace.WriteLine("Erste Information")
' in beide Listener schreiben
Alistener.Flush()
Blistener.Flush()
' BListener deregistrieren
Trace.Listeners.Remove("BListener")
Trace.WriteLine("Zweite Information")
Alistener.Flush()

Sollten Sie versuchen, den Informationsfluss nur über den Aufruf der Flush-Methode auf die einzelnen Listener zu steuern, werden Sie damit wenig Erfolg haben. Denn solange ein Listener registriert ist, werden alle eingehenden Informationen in seinen Puffer geschrieben und beim nächsten Aufruf von Flush an den Abnehmer weitergeleitet.

Die Klasse »EventLogTraceListener«

Alternativ zum TextWriterTraceListener können Sie mit einer Instanz der Klasse EventLogTraceListener Meldungen in das Windows-Ereignisprotokoll schreiben. Auch dieser Listener muss in der Listeners-Auflistung mit der Add-Methode registriert werden. Das Verhalten hinsichtlich der Pufferung der Meldungen ist identisch mit der, die wir im letzten Abschnitt erörtert haben.

Die Klasse EventLogTraceListener stellt drei Konstruktoren bereit:


Public Sub New ()
Public Sub New (String)
Public Sub New (EventLog)

Rufen Sie den parameterlosen Konstruktor auf, müssen alle Eigenschaften konfiguriert werden, bevor Nachrichten an ein Ereignisprotokoll gesendet werden können. Geeigneter erscheint daher der Konstruktor, der eine Zeichenfolge erwartet, die das Ereignisprotokoll namentlich beschreibt. Diagnosemeldungen, die von den Klassen Debug oder Trace geschrieben werden, werden Sie anschließend im Anwendungsprotokoll von Windows wiederfinden.


Dim listener As EventLogTraceListener = _
New EventLogTraceListener("TestLog")
Debug.Listeners.Add(listener)
Debug.WriteLine("Ein Eintrag im Ereignisprotokoll")
listener.Flush()

Mit der dritten Überladung besteht die Möglichkeit, eine Ablaufverfolgungs- oder Debug-Meldung an ein beliebiges Ereignisprotokoll auf der lokalen oder einer anderen Maschine weiterzuleiten. Darüber hinaus können Sie auch ein benutzerdefiniertes Ereignisprotokoll erstellen. Der Konstruktor erwartet dann die Referenz auf ein EventLog-Objekt, mit dem ein Ereignisprotokoll von Windows beschrieben wird.

Im folgenden Codefragment wird ein Ereignisprotokoll namens MyProtocol auf der lokalen Maschine eingerichtet. Die lokale Maschine wird durch eine Zeichenfolge, die nur einen Punkt enthält, beschrieben. Sie könnten aber auch den Namen eines entfernten Rechners angeben.


Dim log As EventLog = New EventLog("MyProtocol", ".")
log.Source = "MyApplication"
Dim listener As EventLogTraceListener = _
New EventLogTraceListener(log)
Debug.Listeners.Add(listener)
Debug.WriteLine("Ein Eintrag im Ereignisprotokoll")
listener.Flush()

Mit der Eigenschaft Source geben Sie einen Bezeichner für die Ereignisquelle an. Innerhalb der Ereignisprotokolle von Windows muss dieser eindeutig sein.

Wollen Sie in Erfahrung bringen, welche Ereignisprotokolle auf der lokalen Maschine eingerichtet sind, können Sie sich über den Aufruf der statischen Methode GetEventLogs der Klasse EventLog ein Array gleichen Typs besorgen und auf jedem Element die Log-Eigenschaft auswerten, die den Namen des Ereignisprotokolls als Zeichenfolge liefert.


Dim myEventLogs() As EventLog = EventLog.GetEventLogs(".")
Dim log As EventLog
For Each log In myEventLogs
Console.WriteLine("Protokollname: " + log.Log)
Next

Sind keine benutzerdefinierten Ereignisprotokolle in der Registrierungsdatenbank eingetragen, sollte die Ausgabe Application, System und Security lauten.

Grundsätzlich sollten Sie sich darüber bewusst sein, dass das Ereignisprotokoll nicht dazu dient, eine große Anzahl allgemeiner Meldungen vorzuhalten. Beschränken Sie daher den Einsatz des EventLogTraceListeners auf wirklich wichtige Meldungen.


Galileo Computing

9.2.5 Steuerung der Protokollierung mit Schaltern  downtop

Ausgaben, die mit den Write-Methoden der Klassen Debug und Trace erzeugt werden, suchen sich grundsätzlich immer ihre Abnehmer in den registrierten Listenern. Das .NET Framework stellt Ihnen darüber hinaus mit zwei Schalterklassen einen weiteren effektiven Mechanismus zur Verfügung, mit dem Ablaufverfolgungs- und Debug-Ausgaben dynamisch gesteuert werden können. Bei den Schalterklassen handelt es sich um BooleanSwitch und TraceSwitch, die beide in Switch eine gemeinsame Basisklasse haben.

Die Klasse »BooleanSwitch«

Die einfachere Schalterklasse ist BooleanSwitch, die einem An-/Ausschalter ähnelt. Mit einer Instanz dieser Klasse lassen sich alle Ablaufverfolgungs- und Debug-Ausgaben gemeinsam aktivieren oder deaktivieren.

Die Klasse hat nur einen Konstruktor, dem Sie den Namen des Schalters und eine Beschreibung übergeben müssen:


Dim mySwitch As BooleanSwitch = _
New BooleanSwitch("MeinSchalter", "In MyApplication")

Standardmäßig sind alle Ausgaben zunächst deaktiviert. Sollen die Ausgaben an die Listener weitergeleitet werden, müssen Sie die Eigenschaft Enabled des BooleanSwitch-Objekts auf True setzen:


mySwitch.Enabled = True

Um die aktuelle Schalterstellung auszuwerten und den weiteren Protokollierungsablauf zu steuern, bieten sich die beiden Methoden WriteIf und WriteLineIf der Klassen Debug und Trace an, z.  B.:


Trace.WriteLineIf(mySwitch.Enabled, "Ablaufverfolgung:" + _
DateTime.Now.ToString());

Nur dann, wenn der Schalter mit Enabled=True aktiviert ist, wird die Meldung mit genauer Datums- und Zeitangabe an alle registrierten Listener geschickt.

Die Schalterstellung in einer Konfigurationsdatei festlegen

Damit sind aber noch nicht alle Möglichkeiten erschöpft. .NET-Anwendungen können um eine optionale Anwendungskonfigurationsdatei erweitert werden, in der nach Bedarf Einstellungen verändert werden können, ohne dass die Anwendung neu kompiliert werden muss. Anwendungskonfigurationsdateien sind XML-Dateien und immer im Stammverzeichnis der Anwendung zu finden. Der Name einer Konfigurationsdatei setzt sich aus dem Namen der Anwendungsdatei und dem Suffix .config zusammen. Lautet die Anwendungsdatei MyApplication.exe, heißt die Konfigurationsdatei demnach MyApplication.exe.config.

Mit den Vorgaben in einer Konfigurationsdatei können Sie jederzeit das Laufzeitverhalten einer Anwendung beeinflussen und steuern, denn eine Konfigurationsdatei wird vor dem Start einer Anwendung ausgewertet – vorausgesetzt, der Anwendung wurde eine Konfigurationsdatei zugeordnet.

Um eine Anwendungskonfigurationsdatei hinzuzufügen, öffnen Sie im Projektmappen-Explorer das Kontextmenü des Projekts und wählen Hinzufügen Neues Element... In der Liste markieren Sie Anwendungskonfigurationsdatei. Nach der Bestätigung wird die neue Komponente der Anwendung mit dem Dateinamen App.config hinzugefügt und im Codeeditor geöffnet. Nachfolgend sehen Sie die vom Visual Studio bereitgestellte Struktur, die bereits die für die Steuerung der Protokollierung mittels Schalter wichtigsten XML-Elemente enthält. Der besseren Übersicht wegen habe ich allerdings auf die ansonsten auch noch vorhandenen Kommentare verzichtet.


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics>
<sources>
<!-- Dieser Abschnitt definiert die Protokollierungskonfiguration für My.Application.Log -->
<source name="DefaultSource" switchName="DefaultSwitch">
<listeners>
<add name="FileLog"/>
<!--<add name="EventLog"/>-->
</listeners>
</source>
</sources>
<switches>
<add name="DefaultSwitch" value="Information" />
</switches>
<sharedListeners>
<add name="FileLog" type="Microsoft.VisualBasic.Logging.FileLogTraceListener, Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"
initializeData="FileLogWriter"/>
<!--<add name="EventLog" type="System.Diagnostics.EventLogTraceListener" initializeData="APPLICATION_NAME"/> -->
</sharedListeners>
</system.diagnostics>
</configuration>

In der Anwendungskonfigurationsdatei werden nicht nur die Schalterstellungen angegeben, sondern darüber hinaus kann das Laufzeitverhalten der gesamten Anwendung maßgeblich beeinflusst werden. In Kapitel 27 werden Sie noch weitere Details zu den Konfigurationsdateien erfahren.

Anstatt ein BooleanSwitch-Objekt mit einer Anweisung im Code zu aktivieren bzw. zu deaktivieren, lässt sich das Verhalten des Schalters in der Anwendungskonfigurationsdatei festlegen. Dazu müssen Sie diese folgendermaßen ändern bzw. ergänzen:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics>
<switches>
<add name="MeinSchalter" value="1" />
</switches>
</system.diagnostics>
...
</configuration>

Innerhalb des switches-Knotens wird mit dem add-Element der entsprechende Schalter festgelegt. Eine Konfigurationsdatei ist nicht nur auf die Steuerung eines Schalters beschränkt. Wollen Sie die Ausgabe über mehrere Schalter steuern, fügen Sie einfach für jeden Schalter ein weiteres add-Element hinzu. Das Attribut name gibt den Namen des Schalters an, das Attribut value die Schalterstellung. Dabei steht 1 für die Aktivierung und 0 für die Deaktivierung des Schalters.

In unserem Beispiel ist der Schalter aktiviert. Deshalb wird die Anwendung auch alle Ausgabemeldungen an die registrierten Listener weiterleiten. Wollen wir die Ausgabemeldungen nicht mehr protokollieren, brauchen wir die Konfigurationsdatei nur mit dem Editor zu öffnen und value auf den Wert 0 zu setzen. Beim nächsten Start der Anwendung ist damit die Protokollierung abgeschaltet.

Beachten Sie, dass die Angabe der Schalterstellung im Programmcode dazu führt, dass die Vorgaben in der Konfigurationsdatei verworfen werden.

Die Klasse »TraceSwitch«

Im Gegensatz zu einem BooleanSwitch-Objekt, das nur die Einstellungen aktiviert und deaktiviert zulässt, können die Ablaufverfolgungs- und Debug-Ausgaben mit einem TraceSwitch-Objekt über die Festlegung mehrerer Ebenen gesteuert werden. Verantwortlich für die Steuerung der Ablaufverfolgung ist die Eigenschaft Level, die vom Typ der Enumeration TraceLevel ist.


Public Property Level As TraceLevel

In TraceLevel werden Ablaufverfolgungsebenen definiert, die Sie der folgenden Tabelle entnehmen können.


Tabelle 9.4     Die Konstanten der Enumeration »TraceLevel«

Member Wert Beschreibung
Off 0 Es werden keine Ausgaben an die Listener weitergegeben.
Error 1 Es werden Ausgaben von Fehlermeldungen an die Listener weitergeleitet.
Warning 2 Es werden Ausgaben von Fehler- und Warnmeldungen an die Listener weitergeleitet.
Info 3 Es werden Ausgaben von Fehler-, Warn- und Informationsmeldungen an die Listener weitergeleitet.
Verbose 4 Es werden alle Meldungen an die Listener weitergeleitet.

Wollen Sie ein TraceSwitch-Objekt erzeugen, müssen Sie dem Konstruktor den Namen des Schalters und eine Beschreibung übergeben. Damit gleicht die Instanziierung der von BooleanSwitch. Um die Ablaufverfolgungsebene festzulegen, weisen Sie der Eigenschaft Level einen Wert zu, z.  B.:


Dim mySwitch As TraceSwitch = _
New TraceSwitch("MeinSchalter", "In MyApplication")
mySwitch.Level = TraceLevel.Warning

Damit in Abhängigkeit von der Ablaufverfolgungsebene mit den Methoden WriteIf und WriteLineIf ein boolescher Wert ausgewertet werden kann, sind in der Klasse TraceSwitch die Eigenschaften

gp  TraceError
gp  TraceWarning
gp  TraceInfo
gp  TraceVerbose

definiert, die je nach Schalterstellung True oder False zurückgeben. Um die Ausgabe der Meldungen zu steuern, müssen Sie in den Methoden WriteIf oder WriteLineIf nur eine der Eigenschaften überprüfen:


Trace.WriteLineIf(mySwitch.TraceWarning, _
"Ablaufverfolgung:" + DateTime.Now.ToString())

Die Einstellung der Eigenschaften Level umfasst gleichzeitig auch die niedriger bewerteten Einstellungen, ausschließlich TraceLevel.Off. Wenn im Code beispielsweise die Ablaufverfolgungsebene auf TraceLevel.Info festgelegt ist, bedeutet dies, dass die Eigenschaften TraceError, TraceWarning und TraceInfo True liefern, während TraceVerbose False ist.

Zur Steuerung des oder der TraceSwitch-Objekte bietet sich ebenfalls die Anwendungskonfigurationsdatei an. Dem Attribut value müssen Sie für eine bestimmte Ablaufverfolgungs- ebene einen zugeordneten Wert übergeben. Sie können diesen der Tabelle 9.4 entnehmen. Die folgende Konfigurationsdatei beschreibt die Einstellung TraceLevel.Info.


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics>
<switches>
<add name="MeinSchalter" value="3" />
</switches>
</system.diagnostics>
...
</configuration>


Galileo Computing

9.2.6 Bedingte Kompilierung  toptop

Die bedingte Kompilierung ermöglicht es, Codeabschnitte oder Methoden nur dann zu kompilieren, wenn ein bestimmtes Symbol definiert ist. Üblicherweise werden bedingte Codeabschnitte dazu benutzt, während der Entwicklungsphase den Zustand der Anwendung zur Laufzeit zu testen. Bevor ein Release-Build der Anwendung erstellt wird, wird das Symbol entfernt. Die Abschnitte, deren Code als bedingt kompilierbar gekennzeichnet ist, werden dann nicht mitkompiliert.

Der folgende Code zeigt ein Beispiel für bedingte Kompilierung:


#Const TEST = 10
Module Module1
Sub Main()
#If TEST = 10 Then
Console.WriteLine("TEST = 10")
#Else
Console.WriteLine("TEST <> 10")
#End If
Console.ReadLine()
End Sub

Mit der Prägprozessordirektiven #dConst wird das Symbol TEST definiert und diesem der Wert 10 zugewiesen. Symbole werden immer vor der ersten Anweisung, die selbst keine #Const-Direktive ist, festgelegt. Eine so beschrieben Direktive gilt nur in der Quellcodedatei, in der sie definiert ist.

Mit #If, #ElseIf oder #Else wird der Wert oder das Vorhandensein des angegebenen Symbols getestet. Stimmt die Bedingung mit der Festlegung des Symbols überein, liefert die Prüfung das Ergebnis True, und der Code wird ausgeführt. Da im Beispielcode das Symbol TEST mit dem Wert 10 definiert ist, wird die Ausgabe lauten:


TEST = 10

Standardmäßig sind in VB-Projekten die beiden Symbole DEBUG und TRACE vordefiniert. Diese Vorgabe ist im Projekteigenschaftsfenster eingetragen und hat anwendungsweite Gültigkeit. Die Symbole beschreiben durch ihre Existenz den booleschen Wert True. Sie können sie löschen oder auch weitere hinzufügen. Öffnen Sie dazu das Eigenschaftsfenster des Projekts, markieren Sie die Lasche Kompilieren, und klicken Sie auf die Schaltfläche Erweiterte Kompilierungsfunktion. Es öffnet sich ein Dialog wie in Abbildung 9.6 gezeigt.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 9.6     Dialog zur Festlegung der Symbole der bedingten Kompilierung

Das Projekteigenschaftsfenster bietet darüber hinaus den Vorteil, dass sich die Symbole einer bestimmten Build-Konfiguration zuordnen lassen. Wählen Sie in der Drop-down-Liste der Karte Kompilieren im Eigenschaftsfenster Konfiguration die Build-Konfiguration aus, für welche die angegebenen Symbole gültig sein sollen. Wenn Sie beispielsweise keine Direktiven im Code angeben, dafür aber der Debug-Konfiguration das Symbol TEST zugeordnet haben, wird der in #If – #End If eingeschlossene Code im Debug-Build mitkompiliert, im Release-Build jedoch nicht. Die im Projekteigenschaftsfenster definierten Direktiven gelten projektweit.

 <<   zurück
  
  Zum Katalog
Zum Katalog: Visual Basic 2005
Visual Basic 2005
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Visual C# 2005






 Visual C# 2005


Zum Katalog: Fortgeschrittene Programmierung mit Visual C# 2005






 Fortgeschrittene
 Programmierung
 mit Visual C# 2005


Zum Katalog: Das Programmierhandbuch SQL Server 2005






 Das Programmier-
 handbuch
 SQL Server 2005


Zum Katalog: Einstieg in Visual Basic 2005






 Einstieg in
 Visual Basic 2005


Zum Katalog: Einstieg in Visual C# 2005






 Einstieg in
 Visual C# 2005


Zum Katalog: Konzepte und Lösungen für Microsoft-Netzwerke






 Konzepte und
 Lösungen für
 Microsoft-Netzwerke


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo








Copyright © Galileo Press 2007
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de